#pragma once

#include <iostream>
#include <string>
#include <set>
#include <cstdio>

#include "Log.hpp"

const std::string sep = " ";

class Command
{
private:
    void LoadConf(const std::string &conf)
    {
        std::ifstream in(conf);
        if (!in.is_open())
        {
            LOG(FATAL, "open %s error \n", conf.c_str());
            return;
        }

        std::string line;
        while (std::getline(in, line))
        {
            if (line.empty())
                continue;

            LOG(DEBUG, "load command [%s] success\n", line.c_str());
            _safe_cmd.insert(line);
        }

        in.close();
    }

public:
    Command(const std::string &cond_path) : _cond_path(cond_path)
    {
        LoadConf(_cond_path);
    }


    std::string PrefixCommand(const std::string &cmd)
    {
        if(cmd.empty()) return std::string();
        auto pos = cmd.find(sep);
        if(pos == std::string::npos) return cmd;
        return cmd.substr(0,pos);
    }
    bool SafeCheck(const std::string &cmd)
    {
        std::string prefix = PrefixCommand(cmd);
        auto iter = _safe_cmd.find(prefix);
        if(iter == _safe_cmd.end()) return false;
        return true;
    }   

    std::string Excute(const std::string &cmd)
    {
        // 远程的指令执行和从前写的shell也是差不多的，只是多了一个远程传输->即网络传输
        // 1. fork & pipe  ----> 以前就是这么写的
        // 2. exec
        // 2.1 dup          ---> 但是现在是多线程代码，exec会直接终止整个进程，则每个线程都会被干掉，所以不能用原来的方法

        std::string result;
        if (SafeCheck(cmd))
        {

            // 引入一个新接口->popen 可以自己执行这个命令，并返回执行结果
            FILE *fp = popen(cmd.c_str(), "r");
            if (fp == nullptr)
            {
                return "failed";
            }
            char buffer[1024];
            while (fgets(buffer, sizeof(buffer), fp) != NULL) // fgets 能够从指定的文件流中读取size大小的数据放到buffer里
            {
                result += buffer;
            }
            pclose(fp);
        }
        else
        {
            result = "坏人\n";
        }
        return result;
    }

    ~Command() {}

private:
    std::string _cmd;

    std::set<std::string> _safe_cmd;
    std::string _cond_path;
};